
#include "common.h"
#include <ctype.h>

#include "bookmark-tree-store.h"

#define BOOKMARK_EDITOR_UI PKGDATADIR "/bmeditor.ui"


static GtkBuilder *builder;
static BookmarkEditor *editor;

static void save_node_change ();

enum {
    COLUMN_NAME,
    COLUMN_ENTRY,
    COLUMNS_BM
};

static void on_entry_changed (GtkWidget *widget, void *user_data);

static int is_string_empty (const char *string)
{
    const char *ptr;
    if (string == NULL)
        return TRUE;
    
    for (ptr = string; *ptr; ptr++) {
        if (!isspace(*ptr))
            return FALSE;
    }

    return TRUE;
}



static void add_item (int is_folder)
{
    GtkTreeIter iter;
    GtkTreeModel *model;
    gftp2_bookmark *new_item;
    GNode *new_node;
  
    new_item = gftp2_bookmark_new ();
    if (is_folder)
        new_item->name = g_strdup ("(New folder)");
    else
        new_item->name = g_strdup ("(New bookmark)");
    new_item->is_folder = is_folder;
    new_node = g_node_new (new_item);
    g_node_append (bookmark_root, new_node);

    gtk_tree_store_append (GTK_TREE_STORE (editor->store), &iter, NULL);
    gtk_tree_store_set (GTK_TREE_STORE (editor->store), &iter,
                        COLUMN_NAME, new_item->name,
                        COLUMN_ENTRY, new_node, -1);

    editor->changed = 1;
    gtk_widget_set_sensitive (GTK_WIDGET (editor->save_button), TRUE);
}

static void add_bookmark_cb (GtkMenuItem *item, gpointer data)
{
    add_item (FALSE);
}

static void add_folder_cb (GtkMenuItem *item, gpointer data)
{
    add_item (TRUE);
}

static void menu_position_under (GtkMenu *menu, 
                                 int *x, int *y,
                                 gboolean *push_in,
                                 gpointer user_data)
{
    GtkWidget *widget;
    
    g_return_if_fail (GTK_IS_BUTTON (user_data));
    g_return_if_fail (GTK_WIDGET_NO_WINDOW (user_data));

    widget = GTK_WIDGET (user_data);
    
    gdk_window_get_origin (widget->window, x, y);
    
    *x += widget->allocation.x;
    *y += widget->allocation.y + widget->allocation.height;

    *push_in = FALSE;
}


static gboolean button_press_cb (GtkWidget *widget,
                                 GdkEventButton *event,
                                 gpointer user_data)
{
    if ((event->type == GDK_BUTTON_PRESS) && event->button == 1) {
        gtk_widget_grab_focus (widget);
        
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
        gtk_menu_popup (editor->add_menu,
                        NULL, NULL, menu_position_under, widget, 
                        event->button, event->time);

        return TRUE;
    }
    return FALSE;
}

static void
menu_deactivate_cb (GtkWidget *widget, gpointer user_data)
{
    GtkWidget *menu_button;
    
    menu_button = GTK_WIDGET (user_data);
        
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menu_button), FALSE);
}

static void delete_cb (GtkMenuItem *item, gpointer data)
{
    GtkWidget *dialog;
    int result;
    GtkTreeIter iter;
    GNode *node;

    if (gtk_tree_selection_get_selected (editor->selection, 
                                         NULL, &iter) == FALSE )
        return;

    gtk_tree_model_get (GTK_TREE_MODEL (editor->store), &iter, 
                        COLUMN_ENTRY, &node, -1);

    dialog = gtk_message_dialog_new (editor->window, GTK_DIALOG_MODAL,
                                     GTK_MESSAGE_QUESTION,
                                     GTK_BUTTONS_YES_NO,
                                     _("Delete bookmark?"));

    result = gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);
    switch (result) {
    case GTK_RESPONSE_YES:
        gtk_tree_store_remove (editor->store, &iter);
        g_node_destroy (node);
        break;
    default:
        return;
    }

    gtk_widget_destroy (dialog);
    editor->changed = 1;
    gtk_widget_set_sensitive (GTK_WIDGET (editor->save_button), TRUE);
}



static GNode *new_bm_root;

static void _reconstruct_bookmark_tree (GNode *parent_node, GtkTreeIter *iter)
{
    GNode *node;
    GNode *origNode;
    GtkTreeIter childIter;
    gftp2_bookmark *bm;

    gtk_tree_model_get (GTK_TREE_MODEL(editor->store), iter, 
                        COLUMN_ENTRY, &origNode, -1);
    g_assert (origNode);
    node = g_node_new (origNode->data);
    g_node_append (parent_node, node);

    bm = (gftp2_bookmark *) node->data;
    gtk_tree_store_set (editor->store, iter, 
                        COLUMN_NAME, bm->name,
                        COLUMN_ENTRY, node, -1);

    if (editor->selected_node == origNode)
        editor->selected_node = node;

    /* bm = (gftp2_bookmark *) childNode->data; */
    /* fprintf (stderr, "Reconstruct %s\n", bm->name); */
    
    if (!gtk_tree_model_iter_children (GTK_TREE_MODEL(editor->store), 
                                       &childIter, iter))
        return;
    
    /* for node's child */
    do {
        _reconstruct_bookmark_tree (node, &childIter);
    } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(editor->store), &childIter));
}

static void reconstruct_bookmark_tree ()
{
    GtkTreeIter iter;
    new_bm_root = g_node_new (NULL);
    
    if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(editor->store), &iter))
        return;

    /* fprintf (stderr, "Reconstruct root\n"); */
    do {
        _reconstruct_bookmark_tree (new_bm_root, &iter);
    } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(editor->store), &iter));
    
    g_node_destroy (bookmark_root);
    bookmark_root = new_bm_root;
}

static void save_button_clicked_cb (GtkButton *button, gpointer data)
{
    if (editor->selected_node_content_changed) {
        save_node_change ();
        editor->selected_node_content_changed = 0;
    }
    reconstruct_bookmark_tree ();
    gftp2_bookmark_save ();
    editor->changed = 0;
    gtk_widget_set_sensitive (GTK_WIDGET (editor->save_button), FALSE);

    refresh_bookmark_menu ();
}

static void connect_button_clicked_cb (GtkButton *button, gpointer data)
{
    gftp2_bookmark *bm;

    if (editor->selected_node == NULL)
        return;

    bm = (gftp2_bookmark *) editor->selected_node->data;
    g_assert (!bm->is_folder);
    
    bookmark_activated_cb (NULL, bm);
}

static void setup_toolbar ()
{
    GtkWidget *menu;
    GtkWidget *add_bookmark;
    GtkWidget *add_folder;
    GtkToggleButton *button;
    GtkWidget *hbox, *arrow, *label;

    /* add button */
    menu = gtk_menu_new ();
    add_bookmark = gtk_menu_item_new_with_mnemonic ("Add _Bookmark");
    add_folder = gtk_menu_item_new_with_mnemonic ("Add _Folder");
    
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), add_bookmark);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), add_folder);
    gtk_widget_show_all (menu);
    editor->add_menu = GTK_MENU (menu);

    g_signal_connect (add_bookmark, "activate",
                      G_CALLBACK (add_bookmark_cb), NULL);

    g_signal_connect (add_folder, "activate",
                      G_CALLBACK (add_folder_cb), NULL);


    button = editor->add_button;
    gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
    g_signal_connect (button, "button_press_event",
                      G_CALLBACK (button_press_cb), NULL);

    hbox = gtk_hbox_new (FALSE, 0);
    gtk_container_add (GTK_CONTAINER (button), hbox);

    label = gtk_label_new ("Add");
    gtk_box_pack_start (GTK_BOX (hbox), label,
                        FALSE, FALSE, 0);
    arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
    gtk_box_pack_end (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);

	g_signal_connect (menu, "deactivate", G_CALLBACK (menu_deactivate_cb), 
                      button);

    /* other button */
    g_signal_connect (editor->delete_button, "clicked",
                      G_CALLBACK (delete_cb), NULL);
    
    g_signal_connect (editor->save_button, "clicked",
                      G_CALLBACK (save_button_clicked_cb), NULL);
    g_signal_connect (editor->connect_button, "clicked",
                      G_CALLBACK (connect_button_clicked_cb), NULL);
}

static void
_add_bookmarks_r (GNode *entry, GtkTreeIter *p_iter)
{
    GNode *childNode;
    gchar *bm_name;
    char *pos;
    GtkTreeIter c_iter;
    gftp2_bookmark *child;

    childNode = entry->children;

    while (childNode != NULL) {
        child = (gftp2_bookmark *) childNode->data;
        bm_name = child->name;

        gtk_tree_store_append (editor->store, &c_iter, p_iter);

        gtk_tree_store_set (editor->store, &c_iter, COLUMN_NAME, bm_name, 
                            COLUMN_ENTRY, childNode, -1);
    
        if (childNode->children != NULL) {
            _add_bookmarks_r (childNode, &c_iter);
        } 

        /* else {
            g_signal_connect (G_OBJECT (menu_item), "activate", 
                              G_CALLBACK (cb_bookmark_activated), childNode);
        }
        */

        childNode = childNode->next;
    }

}

static void
add_bookmarks ()
{
    GtkTreeIter parent;

    if (bookmark_root && bookmark_root->children != NULL) {
        _add_bookmarks_r (bookmark_root, NULL);
    }
}

static void set_blank ()
{
    gtk_entry_set_text (GTK_ENTRY (editor->host_entry), "");
    gtk_entry_set_text (GTK_ENTRY (editor->user_entry), "");
    gtk_entry_set_text (GTK_ENTRY (editor->passwd_entry), "");
    gtk_entry_set_text (GTK_ENTRY (editor->port_entry), "");
}

static void set_sensitivity ()
{
    int value, value_name;
    gftp2_bookmark *bm;

    if (editor->selected_node == NULL) {
        value = FALSE;
        value_name = FALSE;
    } else {
        value_name = TRUE;
        bm = (gftp2_bookmark *) editor->selected_node->data;
        if (bm->is_folder)
            value = FALSE;
        else
            value = TRUE;
    }

    gtk_widget_set_sensitive (GTK_WIDGET (editor->name_entry), value_name);
    gtk_widget_set_sensitive (GTK_WIDGET (editor->host_entry), value);
    gtk_widget_set_sensitive (GTK_WIDGET (editor->user_entry), value);
    gtk_widget_set_sensitive (GTK_WIDGET (editor->passwd_entry), value);
    gtk_widget_set_sensitive (GTK_WIDGET (editor->port_entry), value);
    gtk_widget_set_sensitive (GTK_WIDGET (editor->protocol_combo), value);
    gtk_widget_set_sensitive (GTK_WIDGET (editor->connect_button), value);

    if (editor->changed) {
        gtk_widget_set_sensitive (GTK_WIDGET (editor->save_button), TRUE);
    } else
        gtk_widget_set_sensitive (GTK_WIDGET (editor->save_button), FALSE);
}

static void block_entry_change_signal ()
{
    g_signal_handlers_block_by_func (editor->name_entry, on_entry_changed, NULL);
    g_signal_handlers_block_by_func (editor->host_entry, on_entry_changed, NULL);
    g_signal_handlers_block_by_func (editor->user_entry, on_entry_changed, NULL);
    g_signal_handlers_block_by_func (editor->passwd_entry, on_entry_changed, NULL);
    g_signal_handlers_block_by_func (editor->port_entry, on_entry_changed, NULL);
    g_signal_handlers_block_by_func (editor->protocol_combo, on_entry_changed, NULL);
}

static void unblock_entry_change_signal ()
{
    g_signal_handlers_unblock_by_func (editor->name_entry, on_entry_changed, NULL);
    g_signal_handlers_unblock_by_func (editor->host_entry, on_entry_changed, NULL);
    g_signal_handlers_unblock_by_func (editor->user_entry, on_entry_changed, NULL);
    g_signal_handlers_unblock_by_func (editor->passwd_entry, on_entry_changed, NULL);
    g_signal_handlers_unblock_by_func (editor->port_entry, on_entry_changed, NULL);
    g_signal_handlers_unblock_by_func (editor->protocol_combo, on_entry_changed, NULL);
}

static gboolean reset_bookmark (gftp2_bookmark *bm, gchar *name, 
                                gchar *host, gchar *user,
                                gchar *passwd, int port,
                                gchar *protocol)
{
    int changed = 0;

    if (name != NULL) {
        g_free (bm->name);
        bm->name = name;
        changed = 1;
    }

    if (g_strcmp0 (bm->hostname, host) != 0) {
        g_free (bm->hostname);
        bm->hostname = host;
        changed = 1;
    }
        
    if (g_strcmp0 (bm->user, user) != 0) {
        g_free (bm->user);
        bm->user = user;
        changed = 1;
    }

    if (g_strcmp0 (bm->passwd, passwd) != 0) {
        g_free (bm->passwd);
        bm->passwd = passwd;
        changed = 1;
    }

    if (bm->port != port) {
        bm->port = port;
        changed = 1;
    }

    if (g_strcmp0 (bm->protocol, protocol) != 0) {
        g_free (bm->protocol);
        bm->protocol = protocol;
        changed = 1;
    }

    return changed;
}

static gchar* get_entry_string (GtkEntry *entry)
{
    const gchar *temp;
    gchar *str;

    temp = gtk_entry_get_text (entry);
    if (is_string_empty (temp))
        str = NULL;
    else
        str = g_strstrip (g_strdup (temp));

    return str;
}

static void save_node_change ()
{
    gftp2_bookmark *bm;
    gchar *name, *host, *protocol, *user, *passwd;
    const gchar *temp;
    int port;
    int changed;

    if (editor->selected_node == NULL)
        return;

    bm = (gftp2_bookmark *) editor->selected_node->data;
    
    name = get_entry_string (editor->name_entry);
    host = get_entry_string (editor->host_entry);
    user = get_entry_string (editor->user_entry);
    passwd = get_entry_string (editor->passwd_entry);
    port = atoi (gtk_entry_get_text (editor->port_entry));
    protocol = gtk_combo_box_get_active_text (editor->protocol_combo);
    
    reset_bookmark (bm, name, host, user, passwd, port, protocol);
}

static void
selection_changed_cb (GtkTreeSelection *selection, BookmarkEditor *editor)
{
    GtkTreeModel *model;
    GtkTreeIter iter;
    GNode *node;
    gftp2_bookmark *bm;

    model = GTK_TREE_MODEL (editor->store);

    /* save possible change */
    if (editor->selected_node != NULL && editor->selected_node_content_changed) {
        save_node_change ();
        editor->selected_node_content_changed = 0;
    }

    block_entry_change_signal ();

    if (gtk_tree_selection_get_selected (selection, NULL, &iter) == FALSE)
    { 
        editor->selected_node = NULL;
        set_blank ();
        set_sensitivity ();
        unblock_entry_change_signal ();
        return;
    } else {
        char buf[10];

        gtk_tree_model_get (model, &iter, COLUMN_ENTRY, &node, -1);
        bm = (gftp2_bookmark *)node->data;
        editor->selected_node = node;

        if (bm->is_folder) {
            set_blank ();
            gtk_entry_set_text (editor->name_entry, bm->name);
            set_sensitivity ();
            unblock_entry_change_signal ();
            return;
        }

        set_sensitivity ();
        gtk_entry_set_text (editor->name_entry, bm->name);
        gtk_entry_set_text (GTK_ENTRY (editor->host_entry), 
                            bm->hostname == NULL ? "": bm->hostname);
        gtk_entry_set_text (GTK_ENTRY (editor->user_entry), 
                            (bm->user == NULL) ? "": bm->user);
        gtk_entry_set_text (GTK_ENTRY (editor->passwd_entry), 
                            (bm->passwd == NULL) ? "": bm->passwd);
        snprintf (buf, 10, "%d", bm->port);
        gtk_entry_set_text (GTK_ENTRY (editor->port_entry), buf);

        if (bm->protocol != NULL)
            gtk_combo_box_set_active (GTK_COMBO_BOX (editor->protocol_combo),
                               gftp2_protocol_text_to_index (bm->protocol));
    }
    unblock_entry_change_signal ();
}

/* this signal is received when automatic DnD has been done */
static void on_row_deleted_cb (GtkTreeModel *tree_model,
                               GtkTreePath  *path,
                               gpointer      user_data)
{
    editor->changed = 1;
    gtk_widget_set_sensitive (GTK_WIDGET (editor->save_button), TRUE);
}


static void setup_treeview ()
{
    GtkTreeSelection *selection;
    GtkCellRenderer *renderer;
    GtkTreeViewColumn *column;

    gtk_tree_view_set_headers_visible (editor->treeview, FALSE);
    
    editor->store = GTK_TREE_STORE (bookmark_tree_store_new());
    gtk_tree_view_set_model (editor->treeview, 
                             GTK_TREE_MODEL (editor->store));

    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes (_("Name"),
                                     renderer, "text", COLUMN_NAME, NULL);
    gtk_tree_view_append_column (editor->treeview, column);

    add_bookmarks ();

    selection = gtk_tree_view_get_selection (editor->treeview);
    gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
    g_signal_connect (G_OBJECT (selection), "changed",
                      G_CALLBACK (selection_changed_cb), editor);

    editor->selection = selection;

    g_signal_connect (editor->store, "row_deleted",
                      G_CALLBACK (on_row_deleted_cb), NULL);

    gtk_tree_view_set_reorderable (editor->treeview, TRUE);
}




static void setup_protocol_combo (void)
{
    int i, j, num = 0;
    char *default_protocol;
    GtkWidget *temp_combo;
    GtkListStore *store;    
    GtkCellRenderer *cell;
  
    store = gtk_list_store_new (1, G_TYPE_STRING);
    gtk_combo_box_set_model (GTK_COMBO_BOX (editor->protocol_combo),
                            GTK_TREE_MODEL (store));
    cell = gtk_cell_renderer_text_new ();
    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (editor->protocol_combo), 
                                cell, TRUE);
    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (editor->protocol_combo), 
                                    cell, "text", 0,
                                    NULL);

    /* Add protocols */
    default_protocol = gftp_config_get_string ("General", "default_protocol");
    if (default_protocol == NULL)
        default_protocol = g_strdup ("FTP");

    for (i=0, j=0; gftp_protocols[i].name; i++) {
        if (!gftp_protocols[i].shown)
            continue;
        
        gtk_combo_box_append_text (GTK_COMBO_BOX (editor->protocol_combo),
                                   gftp_protocols[i].name);

        if (default_protocol != NULL &&
            strcmp (gftp_protocols[i].name, default_protocol) == 0)
            num = j;

        j++;
    }
    g_free (default_protocol);

    gtk_combo_box_set_active (GTK_COMBO_BOX (editor->protocol_combo), num);
}


static gboolean delete_event_cb (GtkWidget *widget,
                                 GdkEvent  *event,
                                 gpointer   user_data)
{
    GtkWidget *dialog;
    int result;

    if (editor->changed) {
        dialog = gtk_message_dialog_new (editor->window, GTK_DIALOG_MODAL,
                                         GTK_MESSAGE_QUESTION,
                                         GTK_BUTTONS_YES_NO,
                                         _("Save the changes before closing?"));

        result = gtk_dialog_run (GTK_DIALOG (dialog));
        gtk_widget_destroy (dialog);
        switch (result) {
        case GTK_RESPONSE_YES:
            reconstruct_bookmark_tree ();
            gftp2_bookmark_save ();
            refresh_bookmark_menu ();
            break;
        default:
            break;
        }
    }

    gtk_widget_destroy (GTK_WIDGET(editor->window));
    g_free (editor);
    editor = NULL;
    return FALSE;
}


static void on_entry_changed (GtkWidget *widget, void *user_data)
{
    gtk_widget_set_sensitive (GTK_WIDGET (editor->save_button), TRUE);
    editor->changed = 1;
    editor->selected_node_content_changed = 1;
}

static void setup_edit_area ()
{
    g_signal_connect (editor->name_entry, "changed",
                      G_CALLBACK (on_entry_changed), NULL);

    g_signal_connect (editor->host_entry, "changed",
                      G_CALLBACK (on_entry_changed), NULL);

    g_signal_connect (editor->user_entry, "changed",
                      G_CALLBACK (on_entry_changed), NULL);

    g_signal_connect (editor->passwd_entry, "changed",
                      G_CALLBACK (on_entry_changed), NULL);
    
    g_signal_connect (editor->port_entry, "changed",
                      G_CALLBACK (on_entry_changed), NULL);

    g_signal_connect (editor->protocol_combo, "changed",
                      G_CALLBACK (on_entry_changed), NULL);
}

void
show_bookmark_editor (GtkMenuItem *item, gpointer data)
{
    GError *err = NULL;

    if (editor == NULL) {
        editor = g_malloc0 (sizeof(BookmarkEditor));
        gtk_builder_get_all_widgets_simple (BOOKMARK_EDITOR_UI, 
                                "BMWindow", &editor->window,
                                "treeview", &editor->treeview, 
                                "toolbar", &editor->toolbar,
                                "add_button", &editor->add_button,
                                "delete_button", &editor->delete_button,
                                "name_entry", &editor->name_entry,
                                "host_entry", &editor->host_entry,
                                "protocol_combo", &editor->protocol_combo,
                                "user_entry", &editor->user_entry,
                                "passwd_entry", &editor->passwd_entry,
                                "port_entry", &editor->port_entry,
                                "save_button", &editor->save_button,
                                "connect_button", &editor->connect_button,
                                NULL);
        
        g_signal_connect (editor->window, "delete-event", 
                          G_CALLBACK(delete_event_cb), NULL);
        gtk_window_set_transient_for (editor->window, 
                                      GTK_WINDOW(gftp_interface->window));

        setup_treeview();
        setup_toolbar();
        setup_protocol_combo();
        setup_edit_area ();

        set_sensitivity ();
    }
    
    gtk_widget_show_all (GTK_WIDGET (editor->window));
}
